package com.cdty.chess.service.impl;

import com.cdty.chess.controller.MyWebSocketHandler;
import com.cdty.chess.model.*;
import com.cdty.chess.service.BetCifService;
import com.cdty.chess.vo.BetInfoSticVo;
import com.cdty.chess.vo.BetInfoVo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;

@Service
public class BetCifServiceImpl implements BetCifService {

    @Autowired
    private SseEmitterManager emitterManager;
    @Autowired
    private MyWebSocketHandler webSocketHandler;

    @Autowired
    BetInfoServiceImpl betInfoService;

    @Autowired
    NamedParameterJdbcTemplate template;

    @Override
    public BetCountInfo queryBetCount(String uid, Boolean type) {
        String sql = "";
        MapSqlParameterSource params = new MapSqlParameterSource();
            sql = """
                    SELECT
                    	bci.*
                    from
                    	bet_count_info bci
                    where is_bet !='4'
                    order by
                    	create_time desc
                    limit 1
                """;
        List<BetCountInfo> query = template.query(sql, params, new BeanPropertyRowMapper<BetCountInfo>(BetCountInfo.class));
        BetCountInfo betCountInfo = query.get(0);
        return betCountInfo;
    }

    @Override
    public String upBetStart(BetCountInfo info) {
        // 查询主键
        //更新bet_count_info状态为开盘
        String sql = """
                    update bet_count_info set is_bet =:is_bet,update_time = now(),uid=:uid
                    where id = :bid
                """;
        String message ="";
        if("2".equals(info.getIs_bet())){
            //开盘前  初始化下注上限
            betInfoService.initializeLimit();
            message = "管理员已开盘，可以下注啦";
        }
        if("3".equals(info.getIs_bet())){
            message = "管理员已封盘，请等待开奖";
        }

        try {
            int update = this.template.update(sql, new MapSqlParameterSource("bid", info.getId())
                    .addValue("uid", info.getUid()).addValue("is_bet",info.getIs_bet()) );
            //创建结构化消息对象发送消息到前端，提醒下注或封盘
            long start = System.currentTimeMillis();
            //emitterManager.broadToAll("betAll", messageObj);
            ObjectMapper mapper = new ObjectMapper();
            try {
                String msg = mapper.writeValueAsString(Map.of(
                        "event", "betAll",
                        "data", Map.of("message", message)
                ));
                webSocketHandler.broadcastAll(msg);
            } catch (JsonProcessingException e) {
                // 处理异常
            }
            //webSocketHandler.broadcastAll(message);
            long duration = System.currentTimeMillis() - start;
            System.out.println("执行耗时：" + duration + "ms");
        }catch (Exception e) {
            e.printStackTrace();
        }
        return message;
    }

    @Override
    @Transactional
    public String upBetEnd(BetCountInfo info) {
        try{
        // 查询主键
        //todo 1.更新下注用户的积分
            //1）查询当前对局的所有下注用户
            String betUsql = """
                        select
                        	bi.*,oi.b_dtype,bci.n_order,bci.is_bet,
                        oi.oname,oi.odds,oi.m_count,r_count,cu.uname
                        from
                        	bet_count_info bci
                        inner join bet_info bi on
                        	bci.id = bi.bcid
                        inner join odds_info oi on
                        	oi.b_dtype = bi.single_type
                        inner join cuser cu on cu.uid = bi.uid
                        where  1=1 and bci.id =:id
                        order by
                        	bci.create_time desc
                    """;
            MapSqlParameterSource param = new MapSqlParameterSource();
            param.addValue("id",info.getId());
            List<BetInfoVo> betInfos = template.query(betUsql, param, new BeanPropertyRowMapper<>(BetInfoVo.class));
            //2）修改积分
            if (betInfos.size()==0) {
                return "当前对局无人下注,请重新开盘";
            }
            if (betInfos.get(0).getIs_bet().equals("2")) {
                return "当前对局还未封盘";
            }
            if (betInfos.get(0).getIs_bet().equals("4")) {
                return "当前对局已开奖";
            }
            String numerType = info.getNumer_type(); //中奖号
            String numerName = info.getNumer_type_name(); //中奖号
            BeanPropertySqlParameterSource[] batchArgs = new BeanPropertySqlParameterSource[betInfos.size()];
            //开奖结束更新的数据
            BeanPropertySqlParameterSource[] batInfoEnd = new BeanPropertySqlParameterSource[betInfos.size()];
            BeanPropertySqlParameterSource[] recordInfo = new BeanPropertySqlParameterSource[betInfos.size()];
            List<MessageQueue> qlist = new ArrayList<>();
            BigDecimal scoreSum = new BigDecimal(0); //中奖赔付额
            BigDecimal betSum = new BigDecimal(0); //下注总额
            for (int i = 0; i < betInfos.size(); i++) {
                BetInfoVo betInfoVo = betInfos.get(i);
                StringBuffer message = new StringBuffer();
                Cuser cuser = new Cuser();
                //计算结果
                BigDecimal score= this.getScore(betInfoVo,numerType,message,numerName,betInfoVo.getB_dtype());
                if(score.compareTo(new BigDecimal(0))==1){
                    //中奖消息页发到管理员
                        ObjectMapper mapper = new ObjectMapper();
                        String msg = mapper.writeValueAsString(Map.of(
                                "event", "BetEnd",
                                "data",Map.of("content", "第"+betInfos.get(i).getN_order()+"局开["+numerName+"],"+betInfos.get(i).getUname()+":赢["+score+"]。")
                        ));
                        //持久化消息
                        MessageQueue queue = new MessageQueue();
                        queue.setId(UUID.randomUUID().toString());
                        queue.setEvent_type("BetEnd");
                        queue.setIs_broadcast(false); //表示不给所有人发
                        queue.setContent(msg);
                        //queue.setTarget_uid(""); //管理员都为空
                        queue.setIs_master_only(true); //只给管理员发
                        qlist.add(queue);
                        webSocketHandler.broadToMaster(msg);

                }
                //组装更新赔付信息
                RecordInfo rInfo = new RecordInfo();
                String id = UUID.randomUUID().toString(); // 生成 UUID.
                rInfo.setId(id);
                rInfo.setUid(betInfoVo.getUid());
                rInfo.setUname(betInfoVo.getUname());
                rInfo.setScore(score);
                rInfo.setBtype(Integer.valueOf(betInfos.get(i).getN_order()));
                recordInfo[i] = new BeanPropertySqlParameterSource(rInfo);
                betSum = betSum.add(betInfoVo.getBet_amount());
                scoreSum = scoreSum.add(score);
                betInfos.get(i).setEnd_amount(score);
                //需要更新的用户
                cuser.setUid(betInfoVo.getUid());
                cuser.setBalance(score);
                betInfoVo.setEnd_amount(score);  //赔付额
                batchArgs[i] = new BeanPropertySqlParameterSource(cuser);
                batInfoEnd[i] = new BeanPropertySqlParameterSource(betInfoVo);
                //创建结构化消息对象发送消息到前端，提醒中奖
                Map<String, String> messageObj = new HashMap<>();
                messageObj.put("content", message.toString());
                try {
                    ObjectMapper mapper = new ObjectMapper();
                    String msg = mapper.writeValueAsString(Map.of(
                            "event", "BetEnd",
                            "data",messageObj
                    ));
                    //持久化消息
                    MessageQueue queue = new MessageQueue();
                    queue.setEvent_type("BetEnd");
                    queue.setId(UUID.randomUUID().toString());
                    queue.setContent(msg);
                    queue.setIs_broadcast(false); //表示不给所有人发
                    queue.setTarget_uid(betInfos.get(i).getUid());
                    queue.setIs_master_only(false); //不给管理员发
                    qlist.add(queue);
                    webSocketHandler.broadToUid(betInfos.get(i).getUid(),msg);
                } catch (JsonProcessingException e) {
                    e.getStackTrace();
                    return e.getMessage();
                    // 处理异常
                }
            }
            //无人中奖发送消息到管理员
            if(scoreSum.compareTo(new BigDecimal(0))==0){
                ObjectMapper mapper = new ObjectMapper();
                String msg = mapper.writeValueAsString(Map.of(
                        "event", "BetEnd",
                        "data",Map.of("content", "第"+betInfos.get(0).getN_order()+"局开["+numerName+"]无人中奖。")
                ));
                //持久化消息
                MessageQueue queue = new MessageQueue();
                queue.setId(UUID.randomUUID().toString());
                queue.setEvent_type("BetEnd");
                queue.setIs_broadcast(false); //表示不给所有人发
                queue.setContent(msg);
                //queue.setTarget_uid(""); //管理员都为空
                queue.setIs_master_only(true); //只给管理员发
                qlist.add(queue);
                webSocketHandler.broadToMaster(msg);
            }
            try{
                //批量更新余额
                String sqlbalance = "UPDATE cuser SET balance = balance+:balance  WHERE uid = :uid";
                int[] updateCounts = template.batchUpdate(sqlbalance, batchArgs);
                //更新下注信息
                String sqlupEnd = "UPDATE bet_info SET end_amount = :end_amount  WHERE id = :id";
                int[] endCounts = template.batchUpdate(sqlupEnd, batInfoEnd);
                //更新赔付记录信息
                String rsql = " insert into record_info (id,create_time,score,btype,uid,uname ) values(:id,now(),:score,:btype,:uid,:uname )";
                int[] riCounts = template.batchUpdate(rsql, recordInfo);
                //批量插入信息
                String msql = """
                INSERT INTO message_queue 
                (id, event_type, content, target_uid, is_broadcast, is_master_only, status)
                VALUES 
                (:id, :event_type, :content, :target_uid, :is_broadcast, :is_master_only, :status)
                """;
                // 最简洁的转换方式
                BeanPropertySqlParameterSource[] messages = qlist.stream()
                        .map(BeanPropertySqlParameterSource::new)
                        .toArray(BeanPropertySqlParameterSource[]::new);
                int[] mCounts = template.batchUpdate(msql, messages);
                //更新admin总分
                String upbalance = "UPDATE cuser SET balance = balance-:scoreSum+:noscoreSum  WHERE uname = 'admin'";
                template.update(upbalance, new MapSqlParameterSource("scoreSum", scoreSum).addValue("noscoreSum",betSum) );

            }catch (Exception e){
                e.getMessage();
                e.getStackTrace();
            }

        //3.同时更新局数表,并新增一局
        //更新bet_count_info状态开奖
        String sql = """
                    update bet_count_info 
                        set is_bet = '4',
                        update_time = now(),
                        uid=:uid,
                        numer_type= :numer_type
                    where id = :bid
                """;
        int update = this.template.update(sql, new MapSqlParameterSource("bid", info.getId())
                .addValue("uid", info.getUid())
                .addValue("numer_type",info.getNumer_type()));
        // 2新增对局
        BetCountInfo newInfo = new BetCountInfo();
        //newInfo.setRid(info.getRid());
        newInfo.setIs_bet("1"); //未开盘
        String id = UUID.randomUUID().toString(); // 生成 UUID
        String bCountsql = """
                insert into
                bet_count_info(id,
                create_time,is_bet,dis_num
                )values(:id,
                now(),'1',:dis_num
                )
                """;
            int i = template.update(bCountsql, new MapSqlParameterSource("dis_num", info.getNumer_type()).addValue("id",id));
            //发送消息给所有用户更新下注积分
            List<BetInfoSticVo> betList = betInfoService.getBetList(id);
            ObjectMapper mapper = new ObjectMapper();
            String msgAll = mapper.writeValueAsString(Map.of(
                    "event", "BetAllInfo",
                    "data",betList
            ));
            webSocketHandler.broadcastAll(msgAll);
            return "success";
        }catch (Exception e){
            e.getStackTrace();
            return e.getMessage();
        }
    }

    private BigDecimal getScore(BetInfoVo vo, String numerType, StringBuffer message, String numerName, String bDtype) {
        BigDecimal betAmount = vo.getBet_amount();//下注分数
        BigDecimal odds = vo.getOdds(); //赔率
        String singleMain = vo.getSingle_main(); //主字  逗号隔开的
        String other = vo.getRecovery_other(); //回本字  逗号隔开的


        if (singleMain.contains(numerType)) {
            if(bDtype.equals(OddsEnum.S003.getValue())){  //特殊处理主字三
                BigDecimal divide = betAmount.divide(new BigDecimal(3),2, BigDecimal.ROUND_HALF_UP);
                BigDecimal endsocre = divide.multiply(new BigDecimal(10.5)).setScale(1, RoundingMode.HALF_UP);
                message.append("第"+vo.getN_order()+"局开["+numerName+"],中奖["+endsocre+"]。");
                return endsocre;
            }else{
                BigDecimal endsocre = betAmount.multiply(odds).multiply(new BigDecimal(1.05)).setScale(1, RoundingMode.HALF_UP);
                message.append("第"+vo.getN_order()+"局开["+numerName+"],中奖["+endsocre+"]。");
                return endsocre;
            }

        }else if (other.contains(numerType)) { //回本字中奖
            BigDecimal endsocre = betAmount.multiply(new BigDecimal(1.05)).setScale(1, RoundingMode.HALF_UP);
            message.append("第"+vo.getN_order()+"局开["+numerName+"],回本["+endsocre+"]。");
            return endsocre;
        }else{
            message.append("第"+vo.getN_order()+"局开["+numerName+"]未中奖。");
            return new BigDecimal(0);
        }
    }
    @Override
    public List<ChessInfo> chessInfo() {
        String sql = """
                    select * from chess_info order by n_order
                """;
        MapSqlParameterSource param = new MapSqlParameterSource();

        List<ChessInfo> info = template.query(sql, param, new BeanPropertyRowMapper<>(ChessInfo.class));

        return info;
    }

    @Override
    public List<OddsInfo> oddsInfo(String type) {
        String sql = """
                select * from odds_info where 1=1 
                """;
        MapSqlParameterSource param = new MapSqlParameterSource();

        if (StringUtils.hasText(type)) {
            sql +=" and b_type = :b_type";
            param.addValue("b_type",type);
        }
        sql +=" order by n_order ";
        List<OddsInfo> info = template.query(sql, param, new BeanPropertyRowMapper<>(OddsInfo.class));
        return info;
    }

    @Override
    public int updateOdds(OddsInfo info) {
        String sql = """
                update odds_info set odds = :odds where id = :id
                """;
        MapSqlParameterSource param = new MapSqlParameterSource();
        param.addValue("odds", info.getOdds());
        param.addValue("id", info.getId());
        int update = this.template.update(sql,  param);
        return update;
    }

    @Override
    public Map<String, Object> getbetStic(LocalDate time,Integer page,Integer size,String uid) {
        String sql = """
                select bi.*,bci.numer_type,ci.cname,
                       bci.n_order,cu.uname from bet_info bi 
                    inner join bet_count_info bci on bci.id = bi.bcid
                    inner join cuser cu on cu.uid = bi.uid
                    inner join chess_info ci on ci.id = bci.numer_type
                   where 1=1 condition
                 order by bci.create_time desc
                
                """;
        String condition = "";
        MapSqlParameterSource parameters = new MapSqlParameterSource();
        if(time!=null){
            condition += " and Date(bci.create_time)=:time ";
            parameters.addValue("time", time);
        }
        if(StringUtils.hasText(uid)){
            condition += " and cu.uname like concat('%',:uid,'%') ";
            parameters.addValue("uid", uid);
        }
        sql = sql.replace("condition", condition);
        sql += " LIMIT :size OFFSET :offset";
        int offset = (page - 1) * size;
        parameters.addValue("time", time);
        parameters.addValue("size", size);
        parameters.addValue("offset", offset);
        List<Map<String, Object>> mapList = template.queryForList(sql, parameters);
        String countsql = "SELECT count(1) total FROM ("+sql+") a ";
        Map<String, Object> countMap = template.queryForMap(countsql, parameters);
        Map<String, Object> result = new HashMap<>();
        result.put("total", countMap.get("total"));
        result.put("dataList", mapList);
        return result;
    }

    @Override
    public String upScore(Integer score,String id) {
        String sql = """
                update chess_info set score =:score where id = :id
                """;
        MapSqlParameterSource parameters = new MapSqlParameterSource();
        try {
            parameters.addValue("score", score);
            parameters.addValue("id", id);
            template.update(sql, parameters);
            return "success";
        }catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }

    }
}

